/*
* 所谓递归下降，就是每一种type的token，都对应着它自己的解析方式
* 跟antlr相对应就是，g4规定了文法，按什么样的方式去解析
* 解析到了tokens，遍历tokens
* 根据不同类型的token，来解析，解析完了之后返回
*
*
* 需要拆分：
*   按简单的步骤，一步步来做，才行
*   比如先解析简单语法
*       number boolean string null 都是直接返回的
*       keyword [break, const, continue, if, else, while, for, function, return, this, var]
*               关键词的返回必须要带类型，因为它有特定的操作
*       variable 也是直接返回
*
* */
//可以处理的情况有
// 1
// 1+2
// 1 * (2 + 3)
// add(1, 2)
const visitExpression = (tokens, begin) => {
    // log(tokens)

    let k = begin
    let expTokens = []
    while (k < tokens.length){
       //逗号或者分号,还有后中括号分割表达式
        if(tokens[k].tokenType === TokenType.semicolon || tokens[k].tokenType === TokenType.comma || tokens[k].tokenType === TokenType.bracketRight){
            // log("tokens[k]", tokens[k])
            // if(tokens[k].tokenType === TokenType.comma && tokens[k+1] && tokens[k+1].tokenType === TokenType.parenthesesRight){
            //     k += 1
            // }else {
                k += 1
                break
            // }

        } else if(tokens[k].tokenType === TokenType.variable){
            let v = visitVariabler(tokens, k)
            k += v[1]
            expTokens.push(v[0])
        }else {
            // log(tokens[k])
            expTokens.push(tokens[k])
            k += 1
        }
    }
    // log("expTokens", expTokens)
    return [parserExpToTree(expTokens), k-begin]
}

const visitFunCall = (tokens, begin) => {
    let i = begin
    const token = tokens[i]
    const {tokenValue, tokenType} = token
    let functionCall = {
        type: AstType.ExpressionCall,
    }
    let callee = {
        type: tokenType,
        value: tokenValue,
    }
    functionCall["callee"] = callee
    let k = begin + 2
    let elements = []
    while(k<tokens.length){
        if(tokens[k].tokenValue === ","){
            k += 1
        } else if(tokens[k].tokenValue === ")"){
            break
        }else {
            let element = visitExpression(tokens, k)
            elements.push(element[0])
            k += element[1]
        }
    }


    functionCall["arguments"] = elements
    return [functionCall, k - begin + 1]
}

const visitArrAssign = (tokens, begin) => {
    let i = begin
    const token = tokens[i]
    const {tokenValue, tokenType} = token
    let accessArray = {
        type: AstType.ExpressionMember,
    }
    accessArray["object"] = token
    let propertyToken = tokens[begin + 2]
    accessArray["property"] = propertyToken

    return [accessArray, 4]

}
const visitDotAssign = (tokens, begin) => {
    const dotDict = {}
    let k = begin
    dotDict["type"] = AstType.ExpressionAssignment

    dotDict["left"] = {
        "object": tokens[k],
        "property": tokens[k+2],
        "type": AstType.ExpressionMember,
    }

    dotDict["operator"] = tokens[k+3]

    k += 4
    // log("rest", tokens)
    let exp = visitExpression(tokens, k)
    dotDict["right"] = exp[0]
    k += exp[1]
    //
    return [dotDict, k - begin]

}
const visitDotVariabler = (tokens, begin) => {
    let k = begin
    let v = {
        "object": tokens[k],
        "property": tokens[k+2],
        "type": AstType.ExpressionMember,
    }
    return [v, 3]

}
const visitDotFunctionCall = (tokens, begin) => {
    let k = begin
    let functionCall = {
        type: AstType.ExpressionCall,
    }
    let callee = {
        "object": tokens[k],
        "property": tokens[k+2],
        "type": AstType.ExpressionMember,
    }

    functionCall["callee"] = callee
    k += 4
    let elements = []
    while(k<tokens.length){
        if(tokens[k].tokenValue === ","){
            k += 1
        } else if(tokens[k].tokenValue === ")"){
            break
        }else {
            let element = visitExpression(tokens, k)
            elements.push(element[0])
            k += element[1]
        }
    }


    functionCall["arguments"] = elements
    return [functionCall, k - begin + 1]
}

const visitAssign = (tokens, begin) => {
    let i = begin
    const token = tokens[i]
    // 跳过 = 号, 从i + 3开始
    const v = visitExpression(tokens, i + 2)
    return [{
        left: token,
        right: v[0],
        type: AstType.ExpressionAssignment
    }, v[1] + 2]
}
const visitVariabler = (tokens, begin) => {
    let i = begin
    const token = tokens[i]
    const {tokenValue, tokenType} = token
    //如果是左小括号，就是函数调用 add(1, 2)
    if(tokens[i + 1]){
        const nextToken = tokens[i + 1]
        if(nextToken.tokenType === TokenType.parenthesesLeft){
            return visitFunCall(tokens, begin)
        }else if(nextToken.tokenType === TokenType.bracketLeft){
            // log("数组访问")   只考虑 arr[0]这种情况
            log('visitArrAssign', visitArrAssign(tokens, begin))
            return visitArrAssign(tokens, begin)
        }else if(nextToken.tokenType === TokenType.dot){
            // this.add(1, 2)
            if(tokens[i+3] && tokens[i+3].tokenType ===TokenType.parenthesesLeft){
                return visitDotFunctionCall(tokens, i)
            }else {
                return visitDotVariabler(tokens, i)
            }


            // variabler = visitDotAssign(tokens, i)
        }else {
            return [
                token,
                i - begin + 1, // 消耗了几个token
            ]
        }
    }
     else {
        return [
            token,
            i - begin + 1, // 消耗了几个token
        ]
    }
}
const visitVar = (tokens, begin) => {
    let i = begin
    const varName = tokens[i + 1]
    // 跳过 = 号, 从i + 3开始
    let vBeginToken = tokens[i+3]
    let v = null
    //通过判断等号后的类型
    if([TokenType.number, TokenType.string, TokenType.boolean, TokenType.variable].includes(vBeginToken.tokenType)) {
         v = visitExpression(tokens, i + 3)
    }else if (vBeginToken.tokenType === TokenType.curlyLeft) {
         v = visitObject(tokens, i + 3)
    }else if (vBeginToken.tokenType === TokenType.bracketLeft) {
         v = visitArray(tokens, i + 3)
    }else if(vBeginToken.tokenType === TokenType.keyword && vBeginToken.tokenValue === "function"){
         v = visitFunction(tokens, i + 3)
    }else if(vBeginToken.tokenType === TokenType.keyword && vBeginToken.tokenValue === "class"){
         v = visitClass(tokens, i + 3)
    }else{
        log("这种 var 后边的元素还没有被定义", tokens.slice(tokens[i+3]))
        // var v = ufmi(tokens, i + 3)
    }
    let len = v[1] + 3
    return [{
        kind: 'var',
        variable: varName,
        value: v[0],
        type: AstType.DeclarationVariable
    }, len]
}
const visitArray = (tokens, begin) =>{
    let elements = []
    let k = begin + 1
    while(k<tokens.length){
        if(tokens[k].tokenValue === ","){
            k += 1
        } else if(tokens[k].tokenValue === "]"){
            k += 1
            break
        }else {
            let element = visitExpression(tokens, k)
            elements.push(element[0])
            k += element[1]
        }
    }
    log("k - begin", k - begin)
    return [
        {
            "type":AstType.ExpressionArray,
            "elements": elements,
        },
        k - begin   // 消耗了几个token
    ]
}
const visitObject = (tokens, begin) =>{

    let properties = []
    let k = begin + 1

    while(k<tokens.length){

        if(tokens[k].tokenValue === ":"){
            let key = tokens[k-1]
            let value = visitExpression(tokens, k + 1)
            k += value[1]
            let property = {
                "type":AstType.PropertyObject,
                "key": key,
                "value":value[0]
            }
            properties.push(property)

        } else if(tokens[k].tokenValue === "}"){
            // log("花括号结束")
            break
        }
        k += 1
    }

    return [
        {
            "type":AstType.ExpressionObject,
            "properties": properties,
        },
        k - begin + 1, // 消耗了几个token
    ]
}
const blockStatement = (tokens, begin) =>{
    // log("before", tokens.slice(begin))

    const r = ufmi(tokens, begin)
    // log("block", r)
    // log("r[1]", r[0])
    // log("rest", tokens.slice(begin+r[1]))

    return [{
        type: AstType.StatementBlock,
        body: r[0],
    }, r[1]]
}
const visitIf =  (tokens, begin) => {
    // log("tokens", tokens)
    let ifStatement = {
        type:AstType.StatementIf,
    }
    let k = begin + 2


    let element = visitExpression(tokens, k)
    k += element[1]

    ifStatement["condition"] = element[0]

    k += 2

    let ifBlock = blockStatement(tokens, k)

    k += ifBlock[1]
    // log("rest", tokens.slice(k))

    ifStatement["consequent"] = ifBlock[0]
    ifStatement["alternate"] = {}

    if(tokens[k] && tokens[k].tokenValue === "else"){
        //如果是 if
        if(tokens[k+1] && tokens[k+1].tokenValue === "if"){
            k += 1
            let vIf = visitIf(tokens, k)
            k += vIf[1]
            // log("else if", tokens.slice(k))
            ifStatement["alternate"] = vIf[0]
        }else {
            k += 2
            let elseBlock = blockStatement(tokens, k)
            k += elseBlock[1]
            ifStatement["alternate"] = elseBlock[0]
        }
    }


    return [ifStatement, k - begin]
}
const visitWhile =  (tokens, begin) => {

    let whileStatement = {
        type:AstType.StatementWhile,
    }
    let k = begin + 2
    //定位到condition
    let element = visitExpression(tokens, k)
    whileStatement["condition"] = element[0]
    k += element[1]

    //定位到body
    k += 2
    let block = blockStatement(tokens, k)
    k += block[1]
    whileStatement["body"] = block[0]

    return [whileStatement, k - begin]
}
const visitFor =  (tokens, begin) => {
    log('tokens', tokens)
    let forStatement = {
        type:AstType.StatementFor,
    }
    let k = begin + 2
    //init
    let init = visitVar(tokens, k)
    forStatement["init"] = init[0]
    k += init[1]
    //condition
    let condition = visitExpression(tokens, k)
    forStatement["condition"] = condition[0]
    k += condition[1]
    log("rest", tokens.slice(k))

    // update
    let update = visitAssign(tokens, k)
    forStatement["update"] = update[0]
    k += update[1]

    k += 2

    let block = blockStatement(tokens, k)
    k += block[1]
    forStatement["body"] = block[0]

    return [forStatement, k - begin]
}
const visitFunction =  (tokens, begin) => {

    let functionStatement = {
        type: AstType.ExpressionFunction,
    }
    let k = begin + 2

    let elements = []
    while(k<tokens.length){
        if(tokens[k].tokenValue === ","){
            k += 1
        } else if(tokens[k].tokenValue === ")"){
            k += 1
            break
        }else {
            let element = visitExpression(tokens, k)
            elements.push(element[0])
            k += element[1]
        }
    }
    // log("begin", tokens[k])

    functionStatement["params"] = elements

    k += 1

    let functionBlock = blockStatement(tokens, k)
    // log("fun block", tokens.slice(k,  k + functionBlock[1]))

    k += functionBlock[1]
    functionStatement["body"] = functionBlock[0]
    return [functionStatement, k - begin]

}
const visitClass =  (tokens, begin) => {
    // log("tokens", tokens)
    let classStatement = {
        type: AstType.ExpressionClass,
    }
    let k = begin + 4
    // log("begin", tokens[k])

    let classBlock = blockStatement(tokens, k)
    // log("class block", tokens.slice(k,  k+classBlock[1]))
    k += classBlock[1]
    classStatement["body"] = classBlock[0]
    return [classStatement, k - begin]

}
const popNode = (sOption, sNode) =>{
    //sOption + +
    //sNode 1 2 3
    let node = {
        "type": AstType.ExpressionBinary,
        "operator":  sOption.pop(),
        "right": sNode.pop(),
        "left": sNode.pop(),
    }

    //如果没有运算符的情况直接输出
    if(sOption.length === 0){
        return node
    }else {
        sNode.push(node)
        return popNode(sOption, sNode)
    }

}

const parserExpToTree = (tokens) =>{
    // log("parserExpToTree", tokens)
    let _options = {
        "+": 1,
        "*": 2,
        "-": 1,
        "/": 2,
        "<": 3,
    }
    let sOption = []
    let sNode = []
    let k = 0
    while (k < tokens.length){
        const t = tokens[k]
        let tokenType = t.tokenType

        // if(tokenType === AstType.ExpressionCall || tokenType === TokenType.number || tokenType === AstType.ExpressionMember||tokenType === TokenType.string|| tokenType === TokenType.variable){
        //     sNode.push(t)
        // }else

        //查看是否是操作符号
        if(Object.keys(_options).includes(t.tokenValue)){
            //如过是空直接push
            if(sOption.length === 0){
                sOption.push(t)
            }else {
                let top = sOption[sOption.length -1].tokenValue
                //遇到优先级比较低的先出栈
                if(_options[top] > _options[t.tokenValue]){

                    let node = {
                        "right": sNode.pop(),
                        "type": AstType.ExpressionBinary,
                        "operator":  sOption.pop(),
                        "left": sNode.pop(),
                    }
                    sNode.push(node)
                    sOption.push(t)
                }else {
                    //同级或者低的直接入栈
                    sOption.push(t)
                }
            }
        } else if(t.tokenValue === "("){
            sOption.push(t)
        }else if(t.tokenValue === ")"){
            while (sOption.length !== 0){
                let top = sOption[sOption.length - 1].tokenValue
                if(top === "("){
                    sOption.pop()
                    break
                }else {
                    let node = {
                        "right": sNode.pop(),
                        "type": AstType.ExpressionBinary,
                        "operator":  sOption.pop(),
                        "left": sNode.pop(),
                    }
                    sNode.push(node)
                }

            }

        }else {
            sNode.push(t)
        }

        k += 1
    }

    //如果队列中只有一个元素，直接输出，否则需要展开成树
    if(sOption.length === 0 && sNode.length === 1){
        // log("sNode[0]", sNode[0])
        return sNode[0]
    }else {
        return popNode(sOption, sNode)
    }
}
//现在遇到的情况是，测试一下正好检测一下
const ufmi = (tokens, begin) => {
    let i = begin
    let body = []

    while (i < tokens.length) {
        const token = tokens[i]
        const {tokenValue, tokenType} = token
        // log('当前访问', tokenValue, tokenType)
        if (tokenType === TokenType.keyword) {
            if (tokenValue === 'var') {
                const r = visitVar(tokens, i)
                i += r[1] - 1
                // log("i", i)
                // log(tokens.slice(i))

                // log("r", r)
                body.push(r[0])
            } else if (tokenValue === 'if') {
                let vIf = visitIf(tokens, i)
                body.push(vIf[0])

                i += vIf[1]
            }
            else if (tokenValue === 'while') {
                let v = visitWhile(tokens, i)
                body.push(v[0])
                i += v[1]
                // log(tokens.slice(i))
            }
            else if (tokenValue === 'for') {
                let v = visitFor(tokens, i)
                body.push(v[0])
                i += v[1]
            }
        }
        else if (tokenValue === ")") {
            // log("lalalal )")
            return  [body, i - begin]
        }else if (tokenValue === "}") {
            // log("return body  }", body)
            i += 1
            return  [body, i - begin]
        }
        else if ([TokenType.number, TokenType.variable].includes(tokenType)){
            //判断下一个元素是否存在
            if(tokens[i+1]){
                let nextToken = tokens[i+1]
                //查看下一个字符是否是赋值表达式，是的话就直接赋值
                //否则的情况有 a add(1, 2) arr[0] 暂时三种情况
                let variabler = null
                if(nextToken.tokenType === TokenType.assign ){
                    variabler = visitAssign(tokens, i)
                }else if(nextToken.tokenType === TokenType.dot){
                    // this.a = 1 判断是单独的点调用还是 赋值表达式
                    if(tokens[i+3] && tokens[i+3].tokenType ===TokenType.assign){
                        variabler = visitDotAssign(tokens, i)
                    }else{
                        variabler = visitExpression(tokens, i)
                    }

                }else {
                    // 表达式情况
                    // log("in")
                    variabler = visitExpression(tokens, i)
                    // log("variabler", variabler)
                }
                body.push(variabler[0])
                i += variabler[1] - 1

            }else{
               let  variabler = visitExpression(tokens, i)
                body.push(variabler[0])
                i += variabler[1] - 1
            }
        }

        i += 1
        // log(`i, (${i})`)
        // log('加1的body', body, i)
    }

    //区别
    //内部模块都做好了处理，最后一步-1
    return [body, i - begin]
}

const parser = (tokens) => {
    const r = ufmi(tokens, 0)
    // log(r)
    return r[0]
}

// const test = () => {
//     // test
//     // test_expression_object()
//
// }
//
// const _main = () => {
//     // log('parser')
//     // test()
// }
//
// _main()

//todo 把visitVar里边的expressiond单独抽取出来
// {
//     {
//         value: dduduud;
//         typeo: dudududu,
//     }
// }
// {
//
// }
